package com.passer.toolbox.common.file.util;

import com.passer.toolbox.common.file.constant.FileConstants;
import com.passer.toolbox.common.file.constant.enums.FileType;
import com.passer.toolbox.common.core.util.StringUtils;
import com.passer.toolbox.common.file.filter.FolderFilter;
import com.passer.toolbox.common.file.filter.RegexFilenameFilter;
import com.twmacinta.util.MD5;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class FileUtils {

    public static void classifyFileByMD5(File folder, Map<String, List<String>> md5Map) throws IOException {
        File[] files = folder.listFiles();
        if (files == null || files.length < 1) {
            return;
        }
        for (File file : files) {
            if (file.isDirectory()) {
                classifyFileByMD5(file, md5Map);
                continue;
            }
            String fileMd5 = MD5.asHex(MD5.getHash(file));
            List<String> fileList = md5Map.computeIfAbsent(fileMd5, k -> new ArrayList<>());
            fileList.add(file.getAbsolutePath());
        }
    }

    /**
     * 根据文件 MD5 值将文件夹中的文件进行分类并存储到 Map 中
     * @param folder 待分类文件父目录
     * @return Map<String, List<String>>
     * @throws IOException IO异常
     */
    public static Map<String, List<String>> classifyFileByMD5(File folder) throws IOException {
        Map<String, List<String>> md5Map = new HashMap<>();
        if (folder.isDirectory()) {
            classifyFileByMD5(folder, md5Map);
        }
        return md5Map;
    }

    /**
     * 根据文件名将文件夹中的文件进行分类并存储到 Map 中
     * @param folder 待分类文件父目录
     * @return Map<String, List<String>>
     */
    public static Map<String, List<String>> classifyFileByName(File folder) {
        Map<String, List<String>> folderMap = new HashMap<>();
        Map<String, List<String>> fileMap = new HashMap<>();
        if (folder.isDirectory()) {
            getFileNames(folder, fileMap, null);
            // todo 根据文件名相似度对文件进行分类
            fileMap.forEach((key, value) -> System.out.println(key));
        }
        return folderMap;
    }

    /**
     * 将文件夹中的文件按文件名存储到 Map 中
     * key：文件名
     * value：文件绝对路径列表（兼容重名文件）
     * @param folder 待分类文件夹
     * @param map 存储文件分类的 Map
     * @param fileNameRegex 文件名匹配规则
     */
    public static void getFileNames(File folder, Map<String, List<String>> map, String fileNameRegex) {
        File[] files;
        if (StringUtils.isEmpty(fileNameRegex)) {
            files = folder.listFiles();
        } else {
            files = folder.listFiles(new RegexFilenameFilter(fileNameRegex, FileType.File));
        }

        if (files == null || files.length < 1) {
            return;
        }
        for (File file : files) {
            if (file.isDirectory()) {
                getFileNames(file, map, fileNameRegex);
                continue;
            }
            String fileName = file.getName();
            List<String> fileList = map.computeIfAbsent(fileName, k -> new ArrayList<>());
            fileList.add(file.getAbsolutePath());
        }
    }

    /**
     * 将文件夹中的文件按文件名存储到 Map 中
     * key：文件名
     * value：文件绝对路径列表（兼容重名文件）
     * @param folder 待分类文件夹
     * @param map 存储文件分类的 Map
     */
    public static void getFolderNames(File folder, Map<String, List<String>> map, Pattern folderNameRegex) {
        File[] files = folder.listFiles(new FolderFilter());
        if (files == null || files.length < 1) {
            return;
        }
        for (File file : files) {
            String fileName = file.getName();
            Matcher matcher = folderNameRegex.matcher(fileName);
            if (matcher.find()) {
                List<String> fileList = map.computeIfAbsent(fileName, k -> new ArrayList<>());
                fileList.add(file.getAbsolutePath());
            }
            getFolderNames(file, map, folderNameRegex);
        }
    }

    public static void deleteFolder(File folder, boolean deleteSelf) {
        int i = deleteFolder(folder, 0);
        if (deleteSelf && !folder.delete()) {
            i++;
        }
        if (i > 0) {
            throw new RuntimeException(i + "个文件无法删除");
        }
    }

    public static int deleteFolder(File folder, int failNum) {
        File[] files = folder.listFiles();
        if (files == null) {
            return failNum;
        }
        for (File file : files) {
            if (file.isDirectory()) {
                String[] list = file.list();
                if (list != null && list.length > 0) {
                    failNum = deleteFolder(file, failNum);
                }
            }
            if (!file.delete()) {
                failNum++;
            }
        }
        return failNum;
    }

    /**
     * 文件大小自动转换成合适的单位数值
     *
     * @param size 文件大小（单位：B）
     * @return String 合适的单位的文件大小字符串
     */
    public static String fileSizeUnitChange(long size) {
        if (size < 1024) {
            return size + " B";
        }
        // TODO 自动转换成合适的单位数值
        BigDecimal bigDecimal = new BigDecimal(size);
        if (size >= 1024 * 1024 * 1024) {
            return bigDecimal.divide(new BigDecimal("1073741824"), 2, RoundingMode.HALF_UP) + " GB";
        }
        if (size >= 1024 * 1024) {
            return bigDecimal.divide(new BigDecimal("1048576"), 2, RoundingMode.HALF_UP) + " MB";
        }
        return bigDecimal.divide(new BigDecimal("1024"), 2, RoundingMode.HALF_UP) + " KB";
    }

    public static Optional<File> getNormalSizeFile(File folder) {
        File[] files = folder.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isFile() && file.length() < FileConstants.TEXT_FILE_MAX_LINE) {
                    return Optional.of(file);
                }
            }
        }
        return Optional.empty();
    }

    public static boolean checkFileSize(File file) {
        return file.length() < FileConstants.TEXT_FILE_MAX_LINE;
    }

    public static boolean checkFileSize(String filePath) {
        File file = new File(filePath);
        return file.length() < FileConstants.TEXT_FILE_MAX_LINE;
    }


}
